#include "Matrix.hpp"
#include "InvertibleMatrix.hpp"
#include "Vector.hpp"
#include "Turn.hpp"

#include<iostream>
#include<fstream>
#include<stdexcept>
#include<typeinfo>
#include<string>
using namespace std;

void usage(char* file)
{
	cerr << "Uso: " << file << " <posicion> <salida> [<ultimo> <otros>]" << endl;
}

Vector read_position( char* file )
{
	ifstream f( file );

	unsigned int dim;

	f >> dim; // lee el primer 0
	f >> dim; // obtiene la dimensión

	Vector position(dim);
	f >> position;
	
	return position;
}

Vector read_acum_position( unsigned int n )
{
	ifstream lf( "enemigo.txt" );
	Vector tmp( Turn::dimensions() );
	Vector average( Turn::dimensions() );
	lf >> average;
	for( unsigned int i = 1 ; i < n; i++ )
	{
		lf >> tmp;
		if( i > n-5 )
			average += tmp;
	}
	lf.close();
	return average;
}

Turn** read_history( char* file, unsigned int count )
{
	if( count == 0 ) return NULL;
	ifstream hf( file );
	Turn **history = new Turn*[count];

	// obtiene el historial de turnos
	for( unsigned int t = 0 ; t < count ; t++ )
		history[t] = new Turn( hf );

	hf.close();

	return history;
}

void destroy_history( Turn** history, unsigned int count)
{
	// destruye el historial
	if( history == NULL ) return;
	for( unsigned int t = 0 ; t < count ; t++ )
		delete history[t];
	delete[] history;
}

int main( int argc, char** argv )
{
	if( argc != 3 && argc != 5 )
	{
		usage(argv[0]);
		return 1;
	}

	bool  first_turn = ( argc == 3 );

	// establece el formato de impresión de números
	cout.precision(20);
	cout.setf(ios_base::scientific, ios_base::floatfield);

	ofstream out;
	out.precision(20);
	out.setf(ios_base::scientific,ios_base::floatfield);

	try
	{
		// Obtiene la posición de la nave propia
		Vector x = read_position( argv[1] );

		// Establece las dimensiones y la posición
		Turn::dimensions( x.rows() );
		Turn::position( x );


		Turn* turn;
		InvertibleMatrix A( Turn::dimensions() ); 

		// cálculo de la matriz de ataque
		if( first_turn )
		{
			// en el primer turno elegimos una matriz distinta
			turn = new Turn(1);
			A = SquareMatrix::Hilbert( Turn::dimensions(), 250);
			// Imprime los datos de la posición
			cout << "Dimensiones: " << Turn::dimensions() << endl
		    	 << "Posicion: " << x.transposed();
			remove( "enemigo.txt" );
		}
		else
		{
			// todos los otros turnos usamos los resultados anteriores para inferir la posición
			// obtiene el disparo recibido en el último turno
			ifstream lf( argv[3] );
			Turn last( lf );
			lf.close();

			// obtiene los disparos del oponente de los turnos anteriores
			// Turn** history = read_history( argv[4], last.number()-1 );
			turn = new Turn( last.number()+1 );

			// Obtiene los disparos nuestros de los turnos anteriores
			Vector enemy = read_acum_position( last.number() );

			// calcula la posición del enemigo según su último tiro
			Vector y( last.getMatrix().inverse()*last.getShot() );
			enemy += y;
			enemy /= turn->number()-1;

			A = SquareMatrix::ShootMatrix( Turn::dimensions(), enemy, x, 51+last.number()*10 );
			// destroy_history( history, last.number()-1 );

			out.open( "enemigo.txt", ios::app );
			out << y.transposed();
			out.close();
		}

		turn->setMatrix( A );

		cout << "Turno: " << turn->number() << endl << "------------------------------------" << endl;

		// escribe el archivo de salida
		out.open( argv[2] );
		out << *turn;
		out.close();

		delete turn;
		
	}
	catch( exception& e )
	{
		cerr << "Error: " << e.what() << endl;
	}
	return 0;
}

